/*
 * Copyright 2000-2020 ALPHA LAB.Inc All Rights Reserved.
 */
package com.alphalab.framework.dao; 

import java.io.Serializable; 
import java.util.Collection; 
import java.util.List;
import java.util.Map; 
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.jdbc.Work;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.alphalab.framework.domain.IPersistObject;
import com.alphalab.framework.exception.MoDaoException;
import com.alphalab.framework.log.IMoLog;
import com.alphalab.framework.log.MoLogFactory;
import java.lang.reflect.ParameterizedType;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 实体对象的基础CRUD方法实现，基于hibernate4实现.
 * 对基类方法均加事务，子类调用该方法时就不必加事务，但非基类方法需加.
 * @param <E> 泛型
 * @author allen
 * @version 1.0.0 2017年7月4日
 */ 
@Transactional(rollbackFor=Exception.class)
public class EntityDao<E extends IPersistObject> implements IEntityDao<E> {

	/**
	 * logger.
	 */
	private static final IMoLog logger = MoLogFactory.getLog(EntityDao.class);
	
	/**
	 * entityClass.
	 */
	private final Class<E> entityClass;
	
	/**
	 * sessionFactory.
	 */
	@Autowired
	private SessionFactory sessionFactory;	
	
	/**
	 * EntityDao构造函数，初始化entityClass. 
	 */	
	public EntityDao() { 
		//常见的hibernate的取得子类class的方法，为防子类继承不规范，故加上异常提示处理
		try {
			entityClass = (Class<E>) ((ParameterizedType) getClass()
			        .getGenericSuperclass()).getActualTypeArguments()[0]; 
		} catch (Exception ex) {
			//为防子类继承不规范，故加上异常提示处理
			final String clsName = getClass().getSimpleName();
			final int subLength = 3;
			logger.error("初始化EntityDaoy失败", ex);
			throw new MoDaoException(getClass().getCanonicalName()
					+ "未定义泛型! 继承于:" + EntityDao.class.getCanonicalName()
					+ "的类都必需声明所操作实体的泛型, 如:\npublic class " + clsName
					+ " extends " + EntityDao.class.getSimpleName() + "<"
					+ clsName.substring(0, clsName.length() - subLength)
					+ "> implements I" + clsName + "{\n\t...\n}", ex);
		}
	}
	
	/**
	 * 获取当前session.
	 * @return Session
	 */
	public Session getCurrentSession() { 
		return sessionFactory.getCurrentSession(); 
	}	
	
 	/**
 	 * 关闭当前session.
 	 */
	public void closeSession() {
		if (getCurrentSession() != null) { 
			getCurrentSession().close(); 
		}
	}	 
	
	/**
	 * 保存单个实体.
	 * @param entity 实体
	 */
	@Override
	public void save(final E entity) { 
		getCurrentSession().save(entity);
	}	
	
	/**
	 * 批量保存实体.
	 * @param entities 实体集合
	 */
	@Override
	public void save(final Collection<E> entities) {  
		int i = 0;
		//每50条清理下缓存，避免一级缓存积累过大
		final int num = 50;		
		for (E entity : entities) {
			getCurrentSession().save(entity);
			i++; 
			if (i % num == 0) {
				getCurrentSession().flush();
				getCurrentSession().clear();
			}				
		}		
 
	}		 

	/**
	 * 批量保存实体.
	 * 采用最优的批量保存方法，适应大数据保存
	 * @param tableName 表名
	 * @param fileds 字段名集合
	 * @param insertDatas 行数据集合（map中的string指字段名，Object指字段值）
	 */
	@Override
	public void save(final String tableName, final String[] fileds, 
			final List<Map<String, Object>> insertDatas) throws MoDaoException {  
		//hibernate4获取jdbc的connection的方法
		getCurrentSession().doWork(new Work() {
			@Override
		    public void execute(final Connection conn) { 
		    	try {
					conn.setAutoCommit(false); 
					String columns = "";
					String placeholders = "";
					for (String filed : fileds) { 
						columns += filed + ",";
						placeholders += "?,";
					}			
					columns = columns.substring(0, columns.length() - 1);
					placeholders = placeholders.substring(0, placeholders.length() - 1);
					final String insertSql = "insert into " + tableName + " (" + columns 
							+ ") values (" + placeholders + ")";  
					final PreparedStatement stmt = conn.prepareStatement(insertSql);
					for (int i = 0; i < insertDatas.size(); i++) {  
						//对其进行遍历
						final Map<String, Object> rowData = insertDatas.get(i); 
					    int j = 1;
						for (String filed : fileds) { 
							//无需判断字段类型
							stmt.setObject(j, rowData.get(filed)); 
							j++;
						}	 
					    stmt.addBatch();
					    //每100次执行一次批量提交
					    final int num = 100;
					    if (i % num == 0) {
					        stmt.executeBatch();
					        conn.commit();
					    }	 
					}     
					stmt.executeBatch();
					conn.commit();	  
				} catch (SQLException ex) { 
					logger.error("批量保存失败", ex);
					throw new MoDaoException(ex);
				} 
		    }
		});  
	}	
	
	/**
	 * 删除单个实体.
	 * @param entity 持久化实体.
	 */
	@Override
	public void delete(final E entity) { 
		getCurrentSession().delete(entity);
	}		 
	
	/**
	 * 删除单个实体.
	 * @param id 主键.
	 */
	@Override
	public void delete(final Serializable id) {
		E entity = find(id);
		if (entity != null) {
			delete(entity);
		}
	}	  

	/**
	 * 批量删除实体.
	 * @param entities 持久化实体集合.
	 */
	@Override
	public void delete(final Collection<E> entities) {  
		for (E entity : entities) {
			delete(entity); 
		}	
	} 
	
	/**
	 * 批量删除实体.
	 * @param ids 主键集合.
	 */
	@Override
	public void deleteByPrimaryKeys(final List<? extends Serializable> ids) { 
		for (Serializable id : ids) {
			delete(id);
		}
	} 	 
	
	/**
	 * 更新单个实体.
	 * @param entity 持久化实体.
	 */
	@Override
	public void update(final E entity) { 
		getCurrentSession().update(entity);
	}		
	
	/**
	 * 批量更新实体.
	 * @param entities 持久化实体集合.
	 */
	@Override
	public void update(final Collection<E> entities) {  
		for (E entity : entities) {
			update(entity); 
		}	
	}  
	
	/**
	 * 查询单个实体.
	 * @param id 主键
	 * @return 持久态实体
	 */
	@Override
	public E find(final Serializable id) {
		return (E) getCurrentSession().get(entityClass, id);
	} 
	
	/**
	 * 根据sql执行并返回结果，需加事务才能生效.
	 * @param sql 完整的原生sql语句
	 * @return result 影响的行数
	 */
	@Override
	public int excecuteBySql(final String sql) { 
        final int result;
        final Query query = getCurrentSession().createSQLQuery(sql);    
        result = query.executeUpdate();
        return result;   
	}

	/**
	 * 合并某个持久态到新的持久态.
	 * @param entity 持久化实体.
	 * @return IPersistObject 持久化实体
	 */
	@Override
	public IPersistObject merge(final E entity) { 
		return (IPersistObject) getCurrentSession().merge(entity);
	}

	/**
	 * 将某个持久态的一级缓存清除.
	 * @param entity 持久化实体.
	 */
	@Override
	public void evict(final E entity) {
		getCurrentSession().evict(entity);
	} 
	
}
